import {
	sleep
} from '../'
import { ungzip } from '../pako'
import {
	wscommand_len, wscommand, formatCommand, commandReverse, dispatcher,
	// #ifdef  APP-PLUS||MP-WEIXIN||MP-ALIPAY
	TextDecoder, TextEncoder
	// #endif
} from '.'


type ConnectParams = {
	url : string
	method ?: "GET" | "POST"
	header ?: Record<string, any>
	protocols ?: Array<string>
}
type InitParams = ConnectParams&{
	heartbeatDuration ?: number
}
interface CB {
	(...data: any[]) : void;
}

type sendParams = {
	data ?: Record<string, any> | string | number
	success ?() : void
	fail ?(err : any) : void
	complete ?() : void
}
let hasConnectSocket = false //是否正在连接socket

export default class appSocket {
	readonly initParams : InitParams | {} = {};//初始化参数
	heartbeatDuration = 15//心跳间隔
	private socket : UniNamespace.SocketTask | null = null;
	isCreate = false // WebSocket 是否创建成功
	isConnect = false;//是否已连接
	private isInitiative = false;
	private sendList : [number,sendParams][] = [];//缓存的发送消息队列
	private heartbeatTimer : number | null = null//心跳定时器
	private reconnectTimer : number | null = null//重连定时器
	private newMessageTime : number | null = null
	private dispatcher = new dispatcher() // 事件调度器
	constructor(params : InitParams) {
		this.initParams = params
		this.init(params)
	}
	on(key : string, cb : CB) {
		this.dispatcher.on(key, cb)
	}
	off(key : string, cb : CB) {
		this.dispatcher.off(key, cb)
	}
	private async init({
		url,
		method = "GET",
		header = {},
		protocols = [],
		heartbeatDuration = 5000
	} : InitParams) {
		if(hasConnectSocket)return
		let isReconnect = false
		this.isConnect = false
		this.isCreate = false
		clearTimeout(this.reconnectTimer)
		clearInterval(this.heartbeatTimer)
		if (this.socket) {
			isReconnect = true
			try {
				await this.close()
			} catch (e) {

			}
			await sleep(100)
			this.socket = null
		}

		await this.connectSocket({
			url,
			method,
			header,
			protocols
		})
		this.createSocket(isReconnect)
		this.heartbeatDuration = heartbeatDuration

	}
	private connectSocket({ url,
		method = "GET",
		header = {},
		protocols = [], } : ConnectParams) {
		return new Promise((resolve) => {
			hasConnectSocket = true
			uni.showLoading({
				title:'loading...'
			})
			this.socket = uni.connectSocket({
				url,
				// #ifdef MP-WEIXIN||MP-ALIPAY
				method,
				// #endif
				// #ifdef APP-PLUS||MP-WEIXIN||MP-ALIPAY
				header,
				// #endif
				// #ifdef APP-PLUS||H5||MP-WEIXIN||MP-ALIPAY
				protocols,
				// #endif
				success: () => {
					this.isCreate = true
					resolve({})
				},
				fail: (e) => {
					this.isCreate = false
					this.reconnect()
					this.error({
						type: 'socket创建失败',
						error: e
					})
					resolve({})
				},
				complete: () => {
					hasConnectSocket = false
					uni.hideLoading()
				}
			});
		})
	}
	private createSocket(isReconnect : boolean) {
		if (!this.isCreate) return
		if (isReconnect) {
			console.log('socketReconnect')
			this.dispatcher.fire('reconnect')
		}
		this.socket?.onOpen(() => {
			console.log('onSocketOpen')
			this.isConnect = true
			this.dispatcher.fire('open')
			if (this.sendList) {
				this.sendList.forEach(item => {
					this.send(...item)
				})
			}
			this.sendList = []
			this.heartbeatCheck()
		});
		this.socket?.onError((res) => {
			console.log('onSocketError', res)
			if (!this.isConnect) {
				this.reconnect()
			}

			this.isConnect = false
			this.isInitiative = false
		});
		this.socket?.onMessage(async (res) => {
			this.newMessageTime = new Date().getTime()
			let data : string = ''
			
				const arrayBuffer = res.data;
			
				// log('receive data: ', arrayBuffer, ws)
				let uint8array = null;
				let firstbyte = new Uint8Array(arrayBuffer, 0, 2);
				let firstchar = new TextDecoder('utf-8').decode(firstbyte);
				// let isZipped = false;
				// let isZippedStr = '';
				if (firstchar.indexOf('x') != -1) {
					// 压缩过的
					// isZipped = true;
					// isZippedStr = '(zipped)';
					let zipedUint8array = new Uint8Array(arrayBuffer, 2);
					uint8array = ungzip(zipedUint8array);
				} else {
					uint8array = new Uint8Array(arrayBuffer);
				}
			
				let _data = new TextDecoder('utf-8').decode(uint8array)
				const commandstr = _data.substr(0, wscommand_len);
			
				const commandName = commandReverse[commandstr];
			
			
				// // const bshandler = bs[commandName];
				// const bshandler = wsBs[commandName]//window[commandName];
			
			
				var bodyStr = null;
			
				if (_data.length > wscommand_len) {
					bodyStr = _data.substr(wscommand_len);
					try {
						data = JSON.parse(bodyStr);
					} catch (err) {
					}
				}
			
			console.log(data,commandName)
			this.dispatcher.fire('message', data,commandName)


		});
		this.socket?.onClose(() => {
			console.log('onSocketClose')
			this.reconnect()
			this.dispatcher.fire('appSocketClose')
		})
	}
	send(command : number, params: sendParams) {
		let _data : any = params?.data||''
		const commandstr = formatCommand(command); //-128, 0002, 0012
		let str = commandstr;
		if(_data){
			str = commandstr + JSON.stringify(_data);
		}
		_data = new TextEncoder().encode(str).buffer;
		if (!this.isConnect) {
			this.sendList.push([command,params])
			return
		}
		
		this.socket?.send({
			data: _data,
			success: () => {
				params?.success?.()
			},
			fail: err => {
				this.error({
					type: 'socket发送消息失败',
					error: err
				})
				params?.fail?.(err)
			},
			complete: () => {
				params?.complete?.()
			},
		})
	}
	private error(data : any) {
		this.dispatcher.fire('error', data)
	}
	close(code = 1000) : Promise<void> {
		return new Promise((resolve, reject) => {
			if(hasConnectSocket){
				reject()
			}
			this.isInitiative = true
			this.socket?.close({
				code,
				// reason:'',
				success: () => {
					this.socket = null
					resolve()
				},
				fail: err => {
					this.isInitiative = false
					reject(err)
					this.error({
						type: 'socket关闭失败',
						error: err
					})
				}
			})
		})
	}
	private heartbeatCheck() {
		// this.send(wscommand.HeartbeatReq,{})
		this.heartbeatTimer = setInterval(() => {
			this.send(wscommand.HeartbeatReq,{})
		}, this.heartbeatDuration)
	}
	private reconnect() {
		clearTimeout(this.reconnectTimer)
		clearInterval(this.heartbeatTimer)
		if (!this.isInitiative) {
			this.reconnectTimer = setTimeout(() => {
				this.init(this.initParams as InitParams)
			}, 3000)
		}
	}
	// 检查
	checkSocketState() {
		if (!hasConnectSocket && this.newMessageTime && (new Date().getTime() - this.newMessageTime) > this.heartbeatDuration * 1000) {
			// 当前时间距离上次接收新消息的时间已经超过一个心跳的时间间隔，则代表socket掉线，进行重连
			this.isInitiative = false
			this.reconnect()
		}
	}
}